package cn.schoolwow.quickdao;

import cn.schoolwow.quickdao.domain.entity.Entity;
import cn.schoolwow.quickdao.domain.entity.Property;
import cn.schoolwow.quickdao.domain.util.SynchronizeTableStructureOption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * DAO工具类
 * */
public class DAOUtils {
    private static Logger logger = LoggerFactory.getLogger(DAOUtils.class);

    /**
     * 同步数据库结构
     * @param synchronizeTableStructureOption 同步表结构选项
     * */
    public static void synchronizeTableStructure(SynchronizeTableStructureOption synchronizeTableStructureOption){
        if(null==synchronizeTableStructureOption.source){
            throw new IllegalArgumentException("请指定迁移源数据库!");
        }
        if(null==synchronizeTableStructureOption.target){
            throw new IllegalArgumentException("请指定迁移目标数据库!");
        }
        List<Entity> sourceEntityList = new ArrayList<>();
        if(null==synchronizeTableStructureOption.tableNames||synchronizeTableStructureOption.tableNames.length==0){
            sourceEntityList = synchronizeTableStructureOption.source.getDatabaseEntityList();
        }else{
            for(String tableName:synchronizeTableStructureOption.tableNames){
                Entity sourceEntity = synchronizeTableStructureOption.source.getDatabaseEntity(tableName);
                if(null==sourceEntity){
                    logger.warn("数据库表不存在!表名:"+tableName);
                    continue;
                }
                sourceEntityList.add(sourceEntity);
            }
        }

        //记录需要新增的表,字段以及需要更新的字段
        List<Entity> addEntityList = new ArrayList<>();
        List<Property> addPropertyList = new ArrayList<>();
        Map<Property,Property> updatePropertyMap = new HashMap<>();
        for(Entity sourceEntity:sourceEntityList){
            Entity targetEntity = synchronizeTableStructureOption.target.getDatabaseEntity(sourceEntity.tableName);
            if(null==targetEntity){
                addEntityList.add(sourceEntity);
                continue;
            }
            List<Property> sourcePropertyList = sourceEntity.properties;
            for(Property sourceProperty:sourcePropertyList){
                Property targetProperty = targetEntity.properties.stream().filter(property -> property.column.equalsIgnoreCase(sourceProperty.column)).findFirst().orElse(null);
                if(null==targetProperty){
                    addPropertyList.add(sourceProperty);
                }else if(synchronizeTableStructureOption.diffPropertyPredicate.test(sourceProperty,targetProperty)){
                    updatePropertyMap.put(sourceProperty,targetProperty);
                }
            }
        }

        for(Entity entity:addEntityList){
            if(null!=synchronizeTableStructureOption.createTablePredicate&&!synchronizeTableStructureOption.createTablePredicate.test(entity)){
                continue;
            }
            synchronizeTableStructureOption.target.create(entity);
            if(synchronizeTableStructureOption.executeSQL){
                synchronizeTableStructureOption.target.create(entity);
            }
        }
        for(Property property:addPropertyList){
            if(null!=synchronizeTableStructureOption.createPropertyPredicate&&!synchronizeTableStructureOption.createPropertyPredicate.test(property)){
                continue;
            }
            synchronizeTableStructureOption.target.createColumn(property.entity.tableName, property);
            if(synchronizeTableStructureOption.executeSQL){
                synchronizeTableStructureOption.target.createColumn(property.entity.tableName,property);
            }
        }
        Set<Map.Entry<Property,Property>> propertyEntrySet = updatePropertyMap.entrySet();
        for(Map.Entry<Property,Property> entry:propertyEntrySet){
            if(null!=synchronizeTableStructureOption.updatePropertyPredicate&&!synchronizeTableStructureOption.updatePropertyPredicate.test(entry.getKey(),entry.getValue())){
                continue;
            }
            synchronizeTableStructureOption.target.alterColumn(entry.getKey().entity.tableName, entry.getKey());
            if(synchronizeTableStructureOption.executeSQL){
                synchronizeTableStructureOption.target.alterColumn(entry.getKey().entity.tableName, entry.getKey());
            }
        }
    }

}
